package com.nan.gbd.library.codec.recordcodec;

import android.media.MediaCodec;
import android.media.MediaMuxer;
import android.opengl.EGLContext;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.util.Log;

import com.nan.gbd.library.codec.FinishCallback;
import com.nan.gbd.library.codec.MediaCodecConstant;
import com.nan.gbd.library.codec.configuration.AudioConfiguration;
import com.nan.gbd.library.codec.configuration.Configuration;
import com.nan.gbd.library.codec.configuration.VideoConfiguration;
import com.nan.gbd.library.codec.mediacodec.AudioMediaCodec;
import com.nan.gbd.library.codec.mediacodec.VideoMediaCodec;
import com.nan.gbd.library.codec.audio.AudioCapture;
import com.nan.gbd.library.codec.surface.EglRenderThread;
import com.nan.gbd.library.codec.surface.EglSurfaceView;
import com.nan.gbd.library.utils.AccountConfig;
import com.nan.gbd.library.utils.Logger;

import java.io.IOException;
import java.nio.ByteBuffer;


/**
 * 编解码管理类
 */
public class MediaRecordManager implements MediaMuxerChangeListener {
    private static final String TAG = "MediaEncodeManager";

    private EglSurfaceView.Render eglSurfaceRender;
//    private Surface surface;

    int mediaFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4;
    private MediaMuxer mediaMuxer;

    /*音频硬编码相关参数*/
    private MediaCodec audioCodec;
    private MediaCodec.BufferInfo audioBuffer;

    /*视频硬编码相关参数*/
    private MediaCodec videoCodec;
    private MediaCodec.BufferInfo videoBuffer;


    private AudioCapture audioCapture;

    //时间戳
    private long presentationTimeUs;

    private AudioCodecThread audioThread;
    private VideoCodecThread videoThread;
    private EglRenderThread eglThread;

    /*视频录制相关参数*/
    private FinishCallback finishCallback;
    private VideoConfiguration videoConfig;
    private AudioConfiguration mAudioConfiguration;

    private EGLContext eglContext;
    private int renderMode;


    //文件最大录制大小
    private long FILE_MAX_SIZE_BYTE = Configuration.get().getMaxRecordFileLength() * 1024 * 1024;
    //文件最大录制时长
    private long FILE_MAX_RECORD_DURATION = AccountConfig.getInstance().getMaxDuration() * 1000;
    //文件当前录制大小
    private long mPackedFileSize = 0;
    //开始录制时间
    public long startTime;
    //结束录制时间
    public long endTime;

    public MediaRecordManager(EglSurfaceView.Render eglSurfaceRender, EGLContext eglContext, FinishCallback finishCallback) {
        this.eglSurfaceRender = eglSurfaceRender;
        this.eglContext = eglContext;
        this.renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY;
        this.finishCallback = finishCallback;
    }

    public void initMediaCodec() {
        initVideoCodec();
        initAudioCodec();
        initMediaMuxer();
        initAudioCapture();
    }

    public void initThread() {
        eglThread = new EglRenderThread(videoCodec.createInputSurface(), eglContext, eglSurfaceRender, renderMode, videoConfig.width, videoConfig.height);
        videoThread = new VideoCodecThread(videoCodec, videoBuffer, this);
        audioThread = new AudioCodecThread(audioCodec, audioBuffer, this);
    }

    private void initMediaMuxer() {
        try {
            MediaRecordCaching.getInstance().createCaching();
            String filePath = MediaRecordCaching.getInstance().getRecord().getPath();
            mediaMuxer = new MediaMuxer(filePath, mediaFormat);
        } catch (IOException e) {
            Logger.e("initMediaMuxer: 文件打开失败,path=" + e.getMessage());
        }
    }

    /**
     * 初始化麦克风
     */
    private void initAudioCapture() {
        audioCapture = new AudioCapture();
    }


    private void initAudioCodec() {
        mAudioConfiguration = AudioConfiguration.createDefault();
        audioCodec = AudioMediaCodec.getAudioMediaCodec(mAudioConfiguration);
        audioBuffer = new MediaCodec.BufferInfo();
    }

    private void initVideoCodec() {
        videoConfig = VideoConfiguration.getRecordingConfig();
        videoCodec = VideoMediaCodec.getVideoMediaCodec(videoConfig);
        videoBuffer = new MediaCodec.BufferInfo();
    }

    /**
     * 开始编码
     */
    public void start() {
        if (videoCodec == null) {
            Log.e(TAG, "startEncode: createInputSurface创建失败");
            return;
        }
        eglThread.start();
        videoThread.start();
        audioThread.start();
        audioCapture.start();

        MediaCodecConstant.encodeStart = false;
        MediaCodecConstant.surfaceCreate = true;
        MediaCodecConstant.surfaceChange = true;

        MediaCodecConstant.audioStop = false;
        MediaCodecConstant.videoStop = false;
        Logger.e("startEncode: 视频录制开始");
    }

    /**
     * 编码结束
     */
    public void stop() {
        if (audioThread != null)
            audioThread.stopAudioCodec();
        if (videoThread != null)
            videoThread.stopVideoCodec();
        if (eglThread != null)
            eglThread.stopEglRender();
        if (audioCapture != null)
            audioCapture.stop();

        setEndTime();
        MediaRecordCaching.getInstance().saveCaching(startTime, endTime, mPackedFileSize);
        Logger.e("stopEncode: 视频录制结束");
    }


    @Override
    public void onMediaMuxerChangeListener(int type) {
        if (type == MediaCodecConstant.MUXER_START) {
            setPcmRecordListener();
            mediaMuxer.start();
            setStartTime();
            //标识编码开始
            MediaCodecConstant.encodeStart = true;
            Logger.w("录制视频，开始。。。。。");
        }

        if (type == MediaCodecConstant.MUXER_STOP) {
            destroy();
            if (mPackedFileSize == -1) {
                mPackedFileSize = 0;
                initMediaCodec();
                initThread();
                start();
                Logger.w("录制视频，重启视频录制线程。。。");
            } else {
                if (finishCallback != null) {
                    finishCallback.onFinished(3);
                }
            }
        }
    }

    @Override
    public void onMediaFormatChange(boolean isVideo) {
        if (isVideo) {
            MediaCodecConstant.videoTrackIndex = mediaMuxer.addTrack(videoCodec.getOutputFormat());
            Logger.e("录制视频，videoTrackIndex ：" + MediaCodecConstant.videoTrackIndex);
        } else {
            MediaCodecConstant.audioTrackIndex = mediaMuxer.addTrack(audioCodec.getOutputFormat());
            Logger.e("录制视频，audioTrackIndex ：" + MediaCodecConstant.audioTrackIndex);
        }
    }


    /**
     * 剩余可录制的时长
     */
    long remaining_time;

    @Override
    public void onEncodeOutput(int trackIndex, ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo) {
        Logger.e("输入数据=>trackIndex:" + trackIndex + "  数据长度:" + byteBuffer.remaining());
        if (trackIndex == MediaCodecConstant.videoTrackIndex) {
            long duration = (long) (bufferInfo.presentationTimeUs / 1000.0f);
            remaining_time = FILE_MAX_RECORD_DURATION - duration;
            Logger.w("录制视频，当前已录制时长 ：" + duration + " ms");
            if (remaining_time <= 0 && !videoThread.isStop()) {
                Logger.w("录制视频，当前录制时长以达到最大录制时间。。。");
                stop();
                return;
            }
        }
        if (mPackedFileSize >= FILE_MAX_SIZE_BYTE && !videoThread.isStop()) {
            Logger.w("录制视频，当前录制文件大小以达到最大 file size:" + mPackedFileSize / 1024 + " KB");
            stop();
            mPackedFileSize = -1;
            FILE_MAX_RECORD_DURATION = remaining_time;
            return;
        }
        if (mediaMuxer != null && !videoThread.isStop()) {
            mPackedFileSize += byteBuffer.limit();
            mediaMuxer.writeSampleData(trackIndex, byteBuffer, bufferInfo);
        }
    }

    //录音线程数据回调
    private void setPcmRecordListener() {
        if (audioCapture.getCaptureListener() == null)
            audioCapture.setCaptureListener(new AudioCapture.AudioCaptureListener() {
                @Override
                public void onCaptureListener(byte[] audioSource, int audioReadSize) {
                    if (MediaCodecConstant.audioStop || audioCodec == null) {
                        return;
                    }
                    setPcmSource(audioSource, audioReadSize);
                }
            });
    }

    /**
     * 将音频原始pcm数据送入编码器
     *
     * @param pcmBuffer
     * @param buffSize
     */
    private void setPcmSource(byte[] pcmBuffer, int buffSize) {
        if (audioCodec == null) {
            return;
        }
        try {

            int buffIndex = audioCodec.dequeueInputBuffer(0);
            if (buffIndex < 0) {
                return;
            }
            ByteBuffer byteBuffer;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                byteBuffer = audioCodec.getInputBuffer(buffIndex);
            } else {
                byteBuffer = audioCodec.getInputBuffers()[buffIndex];
            }
            if (byteBuffer == null) {
                return;
            }
            byteBuffer.clear();
            byteBuffer.put(pcmBuffer);
            //presentationTimeUs = 1000000L * (buffSize / 2) / sampleRate
            //一帧音频帧大小 int size = 采样率 x 位宽 x 采样时间 x 通道数
            // 1s时间戳计算公式  presentationTimeUs = 1000000L * (totalBytes / sampleRate/ audioFormat / channelCount / 8 )
            //totalBytes : 传入编码器的总大小
            //1000 000L : 单位为 微秒，换算后 = 1s,
            //除以8     : pcm原始单位是bit, 1 byte = 8 bit, 1 short = 16 bit, 用 Byte[]、Short[] 承载则需要进行换算
            presentationTimeUs += (long) (1.0 * buffSize / (mAudioConfiguration.frequency * mAudioConfiguration.channelCount * (16 / 8)) * 1000000.0);
            Log.d(TAG, "pcm一帧时间戳 = " + presentationTimeUs / 1000000.0f);
            audioCodec.queueInputBuffer(buffIndex, 0, buffSize, presentationTimeUs, 0);
        } catch (IllegalStateException e) {
            //audioCodec 线程对象已释放MediaCodec对象
            Log.d(TAG, "setPcmSource: " + "MediaCodec对象已释放");
        }
    }

    /**
     * 销毁/释放资源
     */
    private void destroy() {
        if (mediaMuxer != null) {
            mediaMuxer.stop();
            mediaMuxer.release();
            mediaMuxer = null;
        }

        audioThread = null;
        videoThread = null;
        eglThread = null;
        audioCapture = null;

        MediaCodecConstant.surfaceCreate = false;
        MediaCodecConstant.surfaceChange = false;
        MediaCodecConstant.audioStop = true;
        MediaCodecConstant.videoStop = true;

        Logger.e("destroy: 释放资源。");
    }

    public void setStartTime() {
        startTime = System.currentTimeMillis() / 1000;
    }

    public void setEndTime() {
        this.endTime = System.currentTimeMillis() / 1000;
    }

}
